#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iostream>
#include <vector>
#include <sstream>
#include <fstream>
#include <limits.h>
#include <queue>
#include <list>
#include <pthread.h>
#include <sys/time.h>
#include "constants.h"
#include <ctime> 
#include <cstdlib>

typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned short ushort;
typedef struct {
	double sentTime;
	int filePosition;
	uint sequenceNumber;
	bool sent;
	bool acked;
} packetInfo;

typedef struct{
	int threadID;
} threadInfo;

using namespace std;

int sockfd, rv, numbytes, protocol;
double timeOut;
bool timeoutPingCalculated, receivedAck = false, allAcked = true;
uint LAR = 0, LFS = 0, SWS, windowIndex = 0, numPackets, acksReceived = 0, maxSequenceNumber;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;
struct timeval tv;
struct timezone tz;
socklen_t addr_len;
pthread_mutex_t print_mutex = PTHREAD_MUTEX_INITIALIZER, windowIndex_mutex = PTHREAD_MUTEX_INITIALIZER;
fstream fin;
priority_queue<uint,deque<uint,allocator<uint> >,greater<uint> > packetsLost;
priority_queue<uint,deque<uint,allocator<uint> >,greater<uint> > packetsDmg;
packetInfo* window;
bool suppressOutput;
uint numPacketsLost = 0;
uint eofSequenceNumber = 0;
uint fileSize =0;
double totalTime=0.0;
int totalAcks=0;
bool randomDamaged = false;
bool randomLost = false;

/** bindSocket - int
  * 	Sets up the socket to use the unreliable data transfer, rather than the 
  *		normal sockets.
  *
  * string hostName - The hostname that we wish to send our data to.
  */
int bindSocket(string hostName){
	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_DGRAM;
	
// get the info needed to set up the socket	
	if ((rv = getaddrinfo(hostName.c_str(), SERVERPORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return 1;
	}
	
	for(p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
			perror("talker: socket");
			continue;
		}
		break;
	}
		
	if (p == NULL) {
		fprintf(stderr, "talker: failed to bind socket\n");
		return 2;
	}
	return 0;
}

/** setSequesnceNumber - void
  * 	Sets up the header of the packet array to include the sequence number.
  *		Since we wanted the sequence number we wanted to support an unsigned int,
  * 	We needed 4 char to cover the range of int, since an int is typically 4 
  *		bytes, and a char is 1 byte.
  *	uchar* data - The packet, with all information
  * uint sequenceNumber - The number that will be inserted into the array header.
  */
void setSequenceNumber(uchar* data, uint sequenceNumber){
	data[0] = (sequenceNumber & 0x000000FF);
	data[1] = ((sequenceNumber & 0x0000FF00) >> 8);
	data[2] = ((sequenceNumber & 0x00FF0000) >> 16);
	data[3] = ((sequenceNumber & 0xFF000000) >> 24);
}

/** getSeqNum - uint
  *		Retrieves the sequence number from the header of the array. This 
  *		is essentially the reverse of the above function, and will pull
  *		4 one byte chars out, and add them together.
  * uchar* data - The packet, with all information.
  */
uint getSeqNum(uchar* data){
	uint seq = (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
	return seq;
}

/** setCheckSum - void
  * 	This method inserts the pre-calculated checksum into the packet 
  *		array.
  * uchar* data - The packet array.
  * ushort checksum - The checksum of the given data.
  */
void setCheckSum(uchar* data, ushort checksum){
	data[4] = (checksum & 0x00FF); 
	data[5] = ((checksum & 0xFF00) >> 8);
}

ushort getChecksum(uchar* data){
	ushort cksum = (data[5] << 8) + data[4];
	return cksum;
}

/** setBOF - void
  *		A method to toggle an area in the packet to signify if the
  *		following data is the beginning of the stream/file/data.
  * uchar* data - The Packet array to have the bit flipped.
  */
void setBOF(uchar* data){
	data[6] = SIG_BOF;
}

/** setEOF - void
  *		A method to toggle the end of file bit, allowing for the
  * 	reciever to know when to terminate the file.
  * uchar* data - The Packet array to have the bit flipped.
  */
void setEOF(uchar* data){
	data[6] = SIG_EOF;
}

/** setPING - void
  *		A method to let the reciever know if the current packet is
  *		being used as a ping timer packet.
  * uchar* data - The packeet array to have the bit flipped.
  */
void setPING(uchar* data){
	data[6] = SIG_PING;
}

/** isPING - bool
  *		A method to see if the packet is a PING packet. This is needed
  *		on the sender to see if the result and end timers.
  * uchar* data - The packet array.
  */
bool isPING(uchar* data){
	bool result = false;
	if(data[4] == SIG_PING){
		result = true;
	}
	return result;
}

/** cksum - ushort
  *		A method to determine what the checksum is for a given message,
  *		and return the value.
  *	uchar* addr - the data to have the checksum calculated on.
  * int count   - The total 
  */
ushort cksum(uchar* addr, int count) {
	/* Compute Internet Checksum for "count" bytes
	*         beginning at location "addr".
	*/
	register long sum = 0;
	uchar first, second;
	ushort shifted;
	while( count > 1 )  {
		/*  This is the inner loop */
		first = *addr;
		*addr++;
		second = *addr;
		*addr++;
		shifted = (first | (second << 8)); 
		sum += shifted;
		count -= 2;
	}
	
	/*  Add left-over byte, if any */
	if( count > 0 ){
			sum += *addr;
	}

	/*  Fold 32-bit sum to 16 bits */
	while (sum>>16)
		sum = (sum & 0xffff) + (sum >> 16);
	return ~sum;
}

/** printdata - void
  * 	Prints the packet array.
  * uchar* data - the packet array
  * int count   - the limit of the array to be calculated.
  */
void printdata(uchar* data, int count){
	cout << " | Data: ";
	for(int i = 0; i < count;i++){
		printf("%X ",data[i]);
	}
	cout << " |" << endl;
}

void printWindow(){
	if(!suppressOutput){
		cout << "WINDOW: [";
		for(uint i = windowIndex; i < (SWS + windowIndex); i++){
			cout << (window[i % SWS].sequenceNumber% maxSequenceNumber) << " (";
			if(window[i % SWS].sent){
				cout << "x";
			} else {
				cout << " ";
			}
			cout << ")(";
			if(window[i % SWS].acked){
				cout << "x";
			} else {
				cout << " ";
			}
			cout << "), ";
		}
		cout << "]" << endl;
		cout<<endl;
	}
}

/** receiveAcks - void
  * 	This method is threaded to allow simultaneous transmission and
  *		recieving of data packets. This specific method should be spawned
  *		as threads and therefore, must be carefully planned when methods
  *		are called. This focuses on acknowledge statements, and allows the
  *		sliding window to move while data is being sent. This will be 
  *		noticably useful for Selective Repeat and Go-back In
  * void * blah - not used, however, required for pthreads.
  */
void *receiveAcks(void *data){
	/*
	in the recvfrom thread, check if the packet you are getting back is a ping
and if so, print something different out
and set a variable
something you can wait for after sending the ping to time it
*/
	int bytesReceived = 0;
	string msg;
	bool delay = false;
	int tempIndex=0;
	while(1){
		uchar ack[5];
		uint seqNum;
		addr_len = sizeof their_addr;
		if ((bytesReceived = recvfrom(sockfd, ack, 5, 0,(struct sockaddr *)&their_addr, &addr_len)) == -1) {
			//done, return threads.
			return (void*)0;
		} else {
			seqNum = getSeqNum(ack);
			while (delay ==false){
				if (seqNum == window[tempIndex].sequenceNumber){
					if (window[tempIndex].sent==true){
						delay = true;
						gettimeofday(&tv, &tz);	//Store time of day in tv struct.
						double currentDelayTime = tv.tv_sec + (tv.tv_usec * .000001);
						totalTime+=(currentDelayTime - window[tempIndex].sentTime)/2;
						totalAcks++;
						tempIndex=0;
					}else{
						delay = true;
						tempIndex=0;
					}
				}else{
					tempIndex++;
				}
			}
			receivedAck = true;
			LAR = seqNum;
			acksReceived++;
			if(!suppressOutput){
			//	cout << "Acks received: " << acksReceived << endl;
			}
			for(uint i = 0; i < SWS; i++){
				if(window[i].sequenceNumber == seqNum){
					window[i].acked = true;
					break;
				}
			}
			pthread_mutex_lock( &windowIndex_mutex );
			if(seqNum == window[windowIndex].sequenceNumber){
				window[windowIndex].sequenceNumber = seqNum + SWS;
				window[windowIndex].acked = false;
				window[windowIndex].sent = false;
				windowIndex = (windowIndex + 1) % SWS;
				for(uint i = 0; i < SWS; i++){
					if(window[windowIndex].acked){
						window[windowIndex].sequenceNumber = window[windowIndex].sequenceNumber + SWS;
						window[windowIndex].acked = false;
						window[windowIndex].sent = false;
						windowIndex = (windowIndex + 1) % SWS;
					} else {
						break;
					}
				}
				allAcked = true;
			} else {
				allAcked = false;
			}
			pthread_mutex_lock( &print_mutex );
			if(!suppressOutput){
				cout << "Received ACK for " << (seqNum % maxSequenceNumber) << endl;
			}
			printWindow();
			pthread_mutex_unlock( &print_mutex );
			pthread_mutex_unlock( &windowIndex_mutex );
		}
	}
	return (void*)0;
}

/** sendBlankPacket - int
  *		This method sends a blank packet, with only header info filled out.
  *		The blank packet is used for the PING timer, since there is no data
  *		that needs to be transmitted.
  * int packetSize - Needs to be filled, since the this variable could change.
  */
int calculateTimeout(int numRoundTrips){
	uchar data[HEADER_SIZE];
	memset(data,0,HEADER_SIZE);
	setPING(data);
	setCheckSum(data, cksum(data,HEADER_SIZE));
	gettimeofday(&tv, &tz);	//Store time of day in tv struct.
	double firstTime = tv.tv_sec + (tv.tv_usec * .000001);
	
	if (sendto(sockfd, data, HEADER_SIZE, 0,p->ai_addr, p->ai_addrlen) == -1) {
		perror("talker: sendto");
		exit(1);
	}
	if (recvfrom(sockfd, data, HEADER_SIZE, 0,(struct sockaddr *)&their_addr, &addr_len) == -1) {
		perror("recvfrom");
		exit(1);
	}
	
	gettimeofday(&tv, &tz);
	timeOut = ((tv.tv_sec + (tv.tv_usec * .000001)) - firstTime) * numRoundTrips * 1000;
	cout << "Timeout: " << timeOut << endl;
	return 0;
}

void initializeWindow(){
	window = new packetInfo[SWS];
	for(uint i = 0; i < SWS; i++){
		window[i].sequenceNumber = i;
		window[i].sentTime = 0;
		window[i].filePosition = 0;
		window[i].sent = false;
		window[i].acked = false;
	}
}

void resetValues(){
	LAR = 0;
	LFS = 0;
	receivedAck = false;
	windowIndex = 0;
	acksReceived = 0;
}

void resendPacket(uint resendIndex, int packetSize, string filePath){
	fin.clear();
	char tmp;
	bool lost;
	uchar* resendData = (uchar*)malloc(packetSize);
	memset(resendData, 0, packetSize);
	int filePositionBefore = fin.tellg();
	setSequenceNumber(resendData,window[resendIndex].sequenceNumber);
	fin.seekg(window[resendIndex].filePosition);
	int i;
	if(window[resendIndex].sequenceNumber == 0){
		//We need to send the file name for the server.
		setBOF(resendData);
		for(i = 0;i<(int)filePath.length();i++){
			resendData[i + HEADER_SIZE] = filePath[i];
		}
		i = i + HEADER_SIZE;
	} else {
		for(i = HEADER_SIZE; i < packetSize && !fin.eof(); i++){
			tmp = fin.get();
			if(!fin.eof()){
					resendData[i] = tmp;
			} else {
					i--;
					setEOF(resendData);
			}
		}
	}
	setCheckSum(resendData,cksum(resendData,i));
	if(window[resendIndex].sequenceNumber == packetsDmg.top() && packetsDmg.size()>0){
		if(!suppressOutput){
			cout << "Damaging packet " << (window[resendIndex].sequenceNumber % maxSequenceNumber) << "." << endl;
		}
		resendData[10] = resendData[10] + 1;	//Damage the packet randomly...
		packetsDmg.pop();
	}
	if (!lost){
		if ((numbytes = sendto(sockfd, resendData, i, 0,p->ai_addr, p->ai_addrlen)) == -1) {
				perror("talker: sendto");
				exit(1);
		}
	}
	allAcked = false;
	LFS = window[resendIndex].sequenceNumber;
	gettimeofday(&tv, &tz);	//Store time of day in tv struct.
	window[resendIndex].sentTime = tv.tv_sec + (tv.tv_usec * .000001);
	window[resendIndex].sent = true;
	pthread_mutex_lock( &print_mutex );
	if(!suppressOutput){
		cout << "Packet " << (window[resendIndex].sequenceNumber % maxSequenceNumber) << " retransmitted. (" << i << " bytes)" << endl;
	}
	printWindow();
	pthread_mutex_unlock( &print_mutex );
	
	pthread_mutex_lock( &print_mutex );
	if(lost){
		if(!suppressOutput){
			cout << "Packet " << (window[resendIndex].sequenceNumber % maxSequenceNumber) << " lost." << endl;
		}
	}
	pthread_mutex_unlock( &print_mutex );
	free(resendData);
	fin.clear();
	fin.seekg(filePositionBefore);
}

/** processPackets - int
  * 	Description...
  * int packetSize - Since the user can enter a packetsize, this must be taken into 
  *					 consideration.
  * string filepath - The path to the file which will be sent.
  */
int processPackets(int packetSize, string filePath, uint numPackets){
	uchar data[packetSize];
	memset(data,0,packetSize);
	int i;
	if (packetSize > MAX_PACKET_SIZE){
		packetSize = MAX_PACKET_SIZE;
	} else {
		packetSize = packetSize;
	}
	char tmp;
	uint sequenceNumber = 0;
	bool lost = false;
	bool sendPacket = false;
	bool sentPacket = false;
	bool eof = false;
	pthread_mutex_unlock( &windowIndex_mutex );
	//Initialize window.
	initializeWindow();
	printWindow();
	string msg;
	while(1){
		if(eof){
			pthread_mutex_lock( &windowIndex_mutex );
			if(window[windowIndex].sequenceNumber == (eofSequenceNumber+1)){
				resetValues();
				lost = false;
				sendPacket = false;
				sequenceNumber = 0;
				eof = false;
				eofSequenceNumber = 0;
				return 0;
			}
			pthread_mutex_unlock( &windowIndex_mutex );
		}
		if(sentPacket && LAR < numPackets){
			gettimeofday(&tv, &tz);	//Store time of day in tv struct.
			double currentTime = tv.tv_sec + (tv.tv_usec * .000001);
			double difference;
			pthread_mutex_lock( &windowIndex_mutex );
			for(uint timeoutIndex = windowIndex; timeoutIndex < windowIndex + SWS; timeoutIndex++){
				if(window[timeoutIndex % SWS].sent && !window[timeoutIndex % SWS].acked){
					difference = ((currentTime - window[timeoutIndex % SWS].sentTime) * 1000);
					if(difference > timeOut){
						if(!suppressOutput){
							cout << "Packet " << (window[timeoutIndex % SWS].sequenceNumber% maxSequenceNumber) << " ******Timed out*******" << endl;
						}
						numPacketsLost++;
						if(protocol == 2){
							for(uint resendIndex = windowIndex; resendIndex < windowIndex + SWS; resendIndex++){
								if(eof){
									if(window[resendIndex % SWS].sequenceNumber <= eofSequenceNumber && window[resendIndex % SWS].sent){
										resendPacket(resendIndex % SWS, packetSize, filePath);
									}
								} else {
									if(window[resendIndex%SWS].sent){
										resendPacket(resendIndex % SWS, packetSize, filePath);
									}
								}
								
							}
						} else {
							resendPacket(timeoutIndex % SWS, packetSize, filePath);
						}
					}
				}
			}
			pthread_mutex_unlock( &windowIndex_mutex );
		}
		if(!eof){
			for(uint j = 0; j < SWS; j++){
				if(window[j].sequenceNumber == sequenceNumber){
					sendPacket = true;
					break;
				} else {
					sendPacket = false;
				}
			}
		} else {
			sendPacket = false;
		}
		if(sendPacket){
			setSequenceNumber(data,sequenceNumber);
			window[sequenceNumber % SWS].sequenceNumber = sequenceNumber;
			if(sequenceNumber == packetsLost.top() && packetsLost.size() > 0) {
				lost = true;
				packetsLost.pop();
			} else {
				lost = false;
			}
			if(sequenceNumber == 0){
				//We need to send the file name for the server.
				setBOF(data);
				for(i = 0;i<(int)filePath.length();i++){
					data[i + HEADER_SIZE] = filePath[i];
				}
				i = i + HEADER_SIZE;
			} else {
				window[sequenceNumber % SWS].filePosition = fin.tellg();
				for(i = HEADER_SIZE; i < packetSize && !fin.eof(); i++){
					tmp = fin.get();
					if(!fin.eof()){
						data[i] = tmp;
					} else {
						i--;
					//	cout << "Setting eof.." << endl;
						setEOF(data);
						eof = true;
						eofSequenceNumber = sequenceNumber;
					}
				}
			}
			setCheckSum(data,cksum(data,i));
			if(sequenceNumber == packetsDmg.top() && packetsDmg.size()>0){
				if(!suppressOutput){
					cout << "Damaging packet " << (sequenceNumber% maxSequenceNumber) << "." << endl;
				}
				data[10] = data[10] + 1;	//Damage the packet randomly...
				packetsDmg.pop();
			}
			if (!lost){
				if ((numbytes = sendto(sockfd, data, i, 0,p->ai_addr, p->ai_addrlen)) == -1) {
					perror("talker: sendto");
					exit(1);
				}
			}
			allAcked = false;
			LFS = sequenceNumber;
			gettimeofday(&tv, &tz);	//Store time of day in tv struct.
			window[sequenceNumber % SWS].sentTime = tv.tv_sec + (tv.tv_usec * .000001);
			window[sequenceNumber % SWS].sent = true;
			pthread_mutex_lock( &print_mutex );
			if(!suppressOutput){
				cout << "Packet " << (sequenceNumber% maxSequenceNumber) << " sent. (" << i << " bytes)"  << endl;
			}
			printWindow();
			pthread_mutex_unlock( &print_mutex );
			
			pthread_mutex_lock( &print_mutex );
			sentPacket = true;
			if(lost){
				if(!suppressOutput){
					cout << "Packet " << (sequenceNumber% maxSequenceNumber) << " lost." << endl;
				}
			}	
			pthread_mutex_unlock( &print_mutex );
			memset(data,0,packetSize);
			sequenceNumber++;
		}
	}
	return 0;
}
 
/** sendFile - int
  * 	Sendfile organizes the user's input and initiates the file read. This 
  *		sorts the inputted data, and creates reads the file incrementally. All
  *		the while packaging them into packets and sending them off.
  * int protocol    - which protocol we should be using
  * string filepath - which file we are sending.
  * int packetSize  - how big are the packets we're sending.
  * uint windowSize - The size of the window.
  */
int sendFile(string filePath, int packetSize){
	string msg;
	
	cout << filePath << endl;
	fin.clear();
	fin.open(filePath.c_str(), ios::in | ios::binary);
	fin.seekg(0, ios::end);
	pthread_mutex_lock( &print_mutex );
	fileSize = fin.tellg();
	cout << "Size: " << fileSize << endl;
	pthread_mutex_unlock( &print_mutex );
	fin.seekg(0);
	
	numPackets = (fileSize / packetSize) + 1;
	if(fileSize % packetSize != 0){
		numPackets++;
	}
	//*********************************************
	ofstream myfile;
	myfile.open ("dmgPackets.txt");
	int random_integer; 
	int range=(numPackets-1)+1; 
	 if (randomLost == true){
		 for(uint index=0; index<(numPackets/10); index++){
			random_integer = int(rand())%range;
			packetsLost.push(random_integer);
			//cout<<" Generating random packet to be lost: " << random_integer<<endl;
			//myfile<<random_integer << " ";
			
		} 
	}
	
	if (randomDamaged == true){
		for(uint index=0; index<(numPackets/10); index++){
			random_integer = int(rand())%range;
			packetsDmg.push(random_integer);
			//cout<<" Generating random packet to be lost: " << random_integer<<endl;
			myfile<<random_integer << " ";
		} 
	}
	myfile.close();
	//*********************************************
	numPacketsLost = 0;
	cout << "Packet Size: " << packetSize << endl;
	cout << "numPackets: " << numPackets << endl;
	int result = 0;
	if(fin == NULL) {
		cout <<"Error opening file." << endl;
		result = 1;
	} else {
		if(packetSize == 0){
			packetSize = (int)DEFAULT_PACKET_SIZE;
		}
		result = processPackets(packetSize, filePath, numPackets);
	}
	fin.close();
	return result;
}

/** main - int
  *		Default main class. Asks for user input, and initiates the
  *		transmission process.
  */
int main(void) {
		string hostName;
        string protocolStr;
		string filePath;
        string packetsLostStr;
        string packetSize;
        string packetsDmgStr;
		bool cont = true;
		string windowSize;
		string contStr;
		string timeoutStr;
		string numRoundTrips;
		string maxSeqNum;
		while(cont){
			//cout << "Host name: ";
			//getline(cin, hostName);	
			cout << "Select protocol: " << endl;
			cout << "\t1 : Stop-and-wait" << endl;
			cout << "\t2 : Go-back-N " << endl;
			cout << "\t3 : Selective Repeat" << endl;
			cout << "Enter number: ";
			getline(cin, protocolStr);
			//cout << "File path: ";
			//getline(cin, filePath);
			
			cout << "Packet Size in KB (0 for default): ";
			getline(cin,packetSize);
			if (atoi(packetSize.c_str())>64){
				packetSize = "64";
			}
			
			if(atoi(protocolStr.c_str()) == 1){
				windowSize = "1";
			} else {
				cout << "Window size: ";
				getline(cin, windowSize);
			}
			cout << "Range of sequence numbers: ";
			getline(cin, maxSeqNum);
			
			while(atoi(windowSize.c_str()) >= ((((double)atoi(maxSeqNum.c_str())) + 1) / 2)){
				cout << "Not enough sequence number space." << endl;
				cout << "SWS: " << atoi(windowSize.c_str()) << endl;
				cout << "MaxSeqNum + 1 / 2: " << ((((double)atoi(maxSeqNum.c_str())) + 1) / 2) << endl;
				cout << "Range of sequence numbers: ";
				getline(cin, maxSeqNum);
			}			
			//**********************************************************
			uint buf;
			cout << "Packets you would like lost (Press R for Random): ";
			getline(cin, packetsLostStr);
			if (packetsLostStr=="r"||packetsLostStr=="R"){
				// generate random packets to be lost
					randomLost= true;
			
			}else{
				istringstream packetsLostSS(packetsLostStr);
				while(packetsLostSS >> buf){
					packetsLost.push(buf);
				}
			}
			cout << "Packets you would like damaged (Press R for Random): ";
			getline(cin, packetsDmgStr);
			if (packetsDmgStr=="r"||packetsDmgStr=="R"){
				randomDamaged = true;
			}else{
				istringstream packetsDmgSS(packetsDmgStr);
				while(packetsDmgSS >> buf){
					packetsDmg.push(buf);
				}
			}
			//****************************************************************************
			
			cout<<"Use Default Timeout? (y/n) ";
			getline(cin, timeoutStr);
			timeoutPingCalculated = false;
			
			if (timeoutStr == "n" || timeoutStr=="N"){
				cout << "Select timeout method: " << endl;
				cout << "\t1 : Milliseconds" << endl;
				cout << "\t2 : Number of round trips" << endl;
				cout << "Enter number: ";
				getline(cin, timeoutStr);
				if (atoi(timeoutStr.c_str()) == 1){
					cout << "Please specify desired time out time (in ms): ";
					getline(cin, timeoutStr);
				} else {	// calculate timeout by round trips
					cout << "Please specify number of round trips before timeout: ";
					timeoutPingCalculated = true;
					getline(cin,numRoundTrips);
				}
				timeOut = atoi(timeoutStr.c_str());
			}
			else{
				timeOut=50;
			}
			
			
			//TESTING VARIABLES
			
			//timeOut=50;
			// tsw-fileout
			//numRoundTrips = "50";
			filePath = "hills.jpg";
			hostName = "data.cs.uwec.edu";
			//packetSize = "30";
			//windowSize = "10";
			//protocolStr = "3";
			//packetsLostStr = "";
			//5 100 843 10000 67 78 79 80 81 98 20000 12000 899 999
			//packetsDmgStr = "";
			// 6 108 9000 68 6000 87 12010
			//timeoutPingCalculated = false;
			
			
			
			suppressOutput = false;


			
			bindSocket(hostName);
			if(timeoutPingCalculated){
				calculateTimeout(atoi(numRoundTrips.c_str()));
			}
			SWS = atoi(windowSize.c_str());
			maxSequenceNumber = atoi(maxSeqNum.c_str());
			pthread_t thread[NUM_THREADS];
			threadInfo ti[NUM_THREADS];
			for(uint i = 0; i < NUM_THREADS; i++){
				ti[i].threadID = i;
				pthread_create(&thread[i], NULL, receiveAcks,(void*)&ti[i]);
			}
			gettimeofday(&tv, &tz);	//Store time of day in tv struct.
			double beforeTime = tv.tv_sec + (tv.tv_usec * .000001);
			protocol = atoi(protocolStr.c_str());
			initializeWindow();
			
			
			if(sendFile(filePath, atoi(packetSize.c_str()) * 1024) > 0){
				cout << "An error occurred. File not transferred." << endl;
			} else {
				pthread_mutex_lock( &print_mutex );
				cout << "File successfully transferred." << endl;
				pthread_mutex_unlock( &print_mutex );
			}
			delete window;
			gettimeofday(&tv, &tz);	//Store time of day in tv struct.
			double afterTime = tv.tv_sec + (tv.tv_usec * .000001);
			double difference = (afterTime - beforeTime) * 1000;
			int minutes = 0, seconds = 0;
			while(difference > 60000){
				difference = difference - 60000;
				minutes++;
			}
			while(difference > 1000){
				difference = difference - 1000;
				seconds++;
			}
			double totalSec = ((double)minutes*60.0)+(double)seconds+((double)difference/1000.0);
			pthread_mutex_lock( &print_mutex );
			cout << "Throughput: " << ((double)fileSize/totalSec)/1000.0<< "KB/S"<<endl;
			cout << "Average Delay Time: "<< totalTime/totalAcks<<" ms."<<endl;
			cout << "Total time for transmission: " << minutes << " min " << seconds << " sec " << (long)difference << " ms." <<  endl;
			cout << "Packets lost in transmission: " << numPacketsLost << endl;
			cout << "Would you like to send another file (y/n)? ";
			pthread_mutex_unlock( &print_mutex );
			getline(cin, contStr);
			if(contStr == "n" || contStr == "N"){
				cont = false;
			}
			
			//for(uint i = 0; i < NUM_THREADS; i++){
			//	pthread_join(thread[i], NULL);
			//}
		}
		freeaddrinfo(servinfo);
		close(sockfd);
        return 0;
};


